
#include <malloc.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <mpi.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/time.h>

#include "log.h"


char directorioLogs[512];
int directoriosCargados = 0;

FILE *obtenerLogFile(char *archivo);
void crearDirectorioDeLog();
char *obtenerNombreArchivo(char *archivo);


/*
 * Implementacion de funcion definida en cabecera
 */
void escribirLog(char *archivo, int linea, char *cadena, ...) {
    
    crearDirectorioDeLog();
    
    FILE *logFile = obtenerLogFile(archivo);
    
    const char *p;
    va_list argp;
    int i;
    long k;
    long long l;
    char *t;
    float w;
    
    time_t ahora = time((time_t *)0);
    
    char obuff[256];
    struct timeval tv;
    gettimeofday(&tv, NULL);
    sprintf(obuff, "%li", tv.tv_usec/1000);
    
    struct tm *tmstruct = localtime(&ahora);
    char buff[256];
    strftime(buff, 255, "%H:%M:%S", tmstruct);
    
    fprintf(logFile, "%s:%s - %s:%d - ",buff, obuff, archivo, linea);
    
    va_start(argp, cadena);

    for(p = cadena; *p != '\0'; p++) {
        
        if(*p != '%') {
            fprintf(logFile, "%c", *p);
            continue;
        }

        switch(*++p) {
            case 'c':
                i = va_arg(argp, int);
                fprintf(logFile, "%i", i);
                break;
            case 'd':
                i = va_arg(argp, int);
                fprintf(logFile, "%i", i);
                break;
            case 'i':
                i = va_arg(argp, int);
                fprintf(logFile, "%i", i);
                break;
            case 'l':
                l = va_arg(argp, long long);
                fprintf(logFile, "%lld", l);
                break;
            case 'k':
                k = va_arg(argp, long);
                fprintf(logFile, "%ld", k);
                break;
            case 'f':
                w = va_arg(argp, double);
                fprintf(logFile, "%f", w);
                break;
            case 's':
                t = va_arg(argp, char *);
                fprintf(logFile, "%s", t);
                free(t);
                break;
            case '%':
                fprintf(logFile, "%%");
                break;
            }
        }

    va_end(argp);
    
    fprintf(logFile, "\n");
    
    fclose(logFile);
    
}


/*
 * Imprime un estado MPI
 */
char *imprimirMensaje(MPI_Status *mensaje) {
    
    char *impr = malloc(sizeof(char) * 1024);
    impr[0] = 0;
    
    sprintf(impr, "MPI_Status {\n\tcount=%i,\n\tcancelled=%i,\n\tMPI_SOURCE=%i,\n\tMPI_TAG=%i,\n\tMPI_ERROR=%i\n}",
            mensaje->count, mensaje->cancelled, mensaje->MPI_SOURCE, mensaje->MPI_TAG, mensaje->MPI_ERROR);
    
    return impr;
    
}

    
/*
 * Imprime un bloque de datos
 */
char *imprimirBloqueInt(int *bloque, long long tamBloque) {
    
    char *impr = malloc(sizeof(char) * 100000);
    impr[0] = 0;
    
    sprintf(impr, "<tam:%lld> [", tamBloque);
    
    char buff[100000];
    
    if (tamBloque>1000) {
        strcat(impr, "...");
    } else {
        long long o;
        for (o=0; o<tamBloque; o++) {
            sprintf(buff, "%i", bloque[o]);
            if (o+1!=tamBloque) {
                strcat(buff, ",");
            }
            strcat(impr, buff);
        }
    }
    
    strcat(impr, "]");
    
    return impr;
    
}


/*
 * Imprime un bloque de datos
 */
char *imprimirBloqueLong(long long *bloque, long long tamBloque) {
    
    char *impr = malloc(sizeof(char)*10000);
    impr[0] = 0;
    
    sprintf(impr, "<tam:%lld> [", tamBloque);
    
    char buff[10000];
    
    if (tamBloque>=50) {
        strcat(impr, "...");
    } else {
        long long o;
        for (o=0; o<tamBloque; o++) {
            sprintf(buff, "%lld", bloque[o]);
            if (o+1!=tamBloque) {
                strcat(buff, ",");
            }
            strcat(impr, buff);
        }
    }
    
    strcat(impr, "]");
    
    return impr;
    
}


/*
 * Imprime un bloque de char
 */
char *imprimirBloqueChar(char *bloque, int tamBloque) {

    char *impr = malloc(sizeof(char) * (tamBloque*10 + 100));
    impr[0] = 0;
    
    sprintf(impr, "{tam:%i} [", tamBloque);
    
    char buff[100];
    
    if (tamBloque>50) {
        strcat(impr, "...");
    } else {
        int o;
        for (o=0; o<tamBloque; o++) {
            sprintf(buff, "%i", bloque[o]);
            if (o+1!=tamBloque) {
                strcat(buff, ",");
            }
            strcat(impr, buff);
        }
    }
    
    strcat(impr, "]");
    
    return impr;
    
}


/*
 * Implementación de la función definida en cabecera.
 */
char *imprimirVector(struct VECTOR *vectorLocal) {
    
    char *vect = NULL;
    long long dimension = obtenerDimension(vectorLocal);
    
    if (dimension<100) {
        int *valores = malloc(sizeof(int)*dimension);
        long long p;
        for (p=0; p<dimension; p++) {
            valores[p] = obtenerValor(vectorLocal, p);
        }
        vect = imprimirBloqueInt(valores, dimension);
        free(valores);
    } else {
        vect = malloc(4*sizeof(char));
        strcpy(vect, "...");
    }
    
    int tamVect = strlen(vect);
    
    char *impr = malloc(sizeof(char) * (tamVect + 60));
    
    impr[0] = 0;
    sprintf(impr, "VECTOR = {\n\tlongitud=%lld,\n\telementos=%s\n}", dimension, vect);
    
    free(vect);
    
    return impr;
    
}


FILE *obtenerLogFile(char *archivo) {
    char *simpleName = obtenerNombreArchivo(archivo);
    char filename[1024];
    sprintf(filename, "%s/%s.log", directorioLogs, simpleName);
    free(simpleName);
    return fopen(filename, "a");
}


void crearDirectorioDeLog() {
    
    if (!directoriosCargados) {
        
        directoriosCargados = 1;
    
        char carpeta[256];

        mkdir("/tmp/cp", S_IRWXU | S_IRGRP | S_IROTH);
        strcpy(carpeta, "/tmp/cp/logs");
        mkdir(carpeta, S_IRWXU | S_IRGRP | S_IROTH);

        time_t ahora = time((time_t *)0);
        struct tm *tmstruct = localtime(&ahora);
        char buff[256];
        strftime(buff, sizeof(carpeta), "/%Y-%m-%d.%X", tmstruct);
        strcat(carpeta, buff);
        mkdir(carpeta, S_IRWXU | S_IRGRP | S_IROTH);

        int nodoId;
        MPI_Comm_rank(MPI_COMM_WORLD, &nodoId);
        char obuff[256];
        sprintf(obuff, "/%i", nodoId);
        strcat(carpeta, obuff);

        strcpy(directorioLogs, carpeta);
        mkdir(directorioLogs, S_IRWXU | S_IRGRP | S_IROTH);
        
    }
    
}


char *obtenerNombreArchivo(char *archivo) {
    
    int longitud = strlen(archivo);
    int fin = longitud-1;
    int inicio = 0;;
    
    int i;
    for (i=fin; i>=0; i--) {
        if (archivo[i]=='/') {
            inicio = i+1;
            break;
        }
    }
    
    int longNombre = 512;
    char *nombre = malloc((longNombre+1) * sizeof(char));
    strcpy(nombre, archivo+inicio);
    
    return nombre;
    
}